import Foundation
import Path
import TuistAutomation
import TuistLoader
import TuistServer
import XcodeGraph
import XCTest

#if canImport(TuistCacheEE)
    import TuistCacheEE
#endif

@testable import TuistCore
@testable import TuistGenerator
@testable import TuistKit
@testable import TuistTesting

final class GraphMapperFactoryTests: TuistUnitTestCase {
    var subject: GraphMapperFactory!

    override func setUp() {
        super.setUp()
        subject = GraphMapperFactory()
    }

    override func tearDown() {
        subject = nil
        super.tearDown()
    }

    func test_default_contains_the_update_workspace_projects_graph_mapper() {
        // When
        let got = subject.default(config: .test())

        // Then
        XCTAssertContainsElementOfType(got, UpdateWorkspaceProjectsGraphMapper.self)
    }

    func test_default_contains_the_explicit_dependency_graph_mapper_when_explcit_dependencies_are_enforced() {
        // When
        let got = subject.default(
            config: .test(project: .generated(.test(generationOptions: .test(enforceExplicitDependencies: true))))
        )

        // Then
        XCTAssertContainsElementOfType(got, ExplicitDependencyGraphMapper.self)
    }

    func test_default_does_not_contain_the_explicit_dependency_graph_mapper() {
        // When
        let got = subject.default(config: .test())

        // Then
        XCTAssertDoesntContainElementOfType(got, ExplicitDependencyGraphMapper.self)
    }

    func test_default_contains_the_modulemap_mapper() {
        // When
        let got = subject.default(config: .test())

        // Then
        XCTAssertContainsElementOfType(got, ModuleMapMapper.self)
    }
}

#if canImport(TuistCacheEE)
    final class CacheGraphMapperFactoryTests: TuistUnitTestCase {
        private var subject: CacheGraphMapperFactory!
        private var cacheStorage: MockCacheStoring!

        override func setUp() {
            super.setUp()
            cacheStorage = MockCacheStoring()
            subject = CacheGraphMapperFactory(contentHasher: ContentHasher())
        }

        override func tearDown() {
            subject = nil
            cacheStorage = nil
            super.tearDown()
        }

        func test_generation_contains_the_graph_mapper_to_auto_generate_schemes() {
            // Given
            let includedTargets: Set<TargetQuery> = Set([.named("MyTarget")])

            // When
            let got = subject.generation(
                config: .test(project: .testGeneratedProject()),
                cacheProfile: .onlyExternal,
                cacheSources: includedTargets,
                configuration: "Debug",
                cacheStorage: cacheStorage
            )

            // Then
            let mapper = XCTAssertContainsElementOfType(got, AutogeneratedWorkspaceSchemeGraphMapper.self)
            XCTAssertNotNil(mapper)
        }

        func test_cache_contains_the_filter_target_dependenies_tree_graph_mapper() {
            // Given
            let includedTargets: Set<TargetQuery> = Set([.named("MyTarget")])

            // When
            let got = subject.binaryCacheWarming(
                config: .test(),
                cacheSources: includedTargets,
                configuration: "Debug",
                cacheStorage: cacheStorage
            )

            // Then
            let mapper = XCTAssertContainsElementOfType(got, FocusTargetsGraphMappers.self)
            XCTAssertEqual(mapper?.includedTargets, includedTargets)
        }

        func test_cache_contains_the_tree_shaking_mapper() {
            // Given
            let includedTargets: Set<TargetQuery> = Set([.named("MyTarget")])

            // When
            let got = subject.binaryCacheWarming(
                config: .test(),
                cacheSources: includedTargets,
                configuration: "Debug",
                cacheStorage: cacheStorage
            )

            // Then
            XCTAssertContainsElementOfType(
                got, TreeShakePrunedTargetsGraphMapper.self, after: FocusTargetsGraphMappers.self
            )
        }

        func test_focus_contains_the_filter_target_dependenies_tree_graph_mapper() {
            // Given
            let config = Tuist.test()
            let cacheSources: Set<TargetQuery> = Set([.named("MyTarget")])

            // When
            let got = subject.generation(
                config: config,
                cacheProfile: .none,
                cacheSources: cacheSources,
                configuration: "Debug",
                cacheStorage: cacheStorage
            )

            // Then
            let mapper = XCTAssertContainsElementOfType(got, FocusTargetsGraphMappers.self)
            XCTAssertEqual(mapper?.includedTargets, cacheSources)
        }

        func test_focus_contains_the_cache_tree_shaking_graph_mapper() {
            // Given
            let config = Tuist.test()
            let cacheSources: Set<TargetQuery> = Set([.named("MyTarget")])

            // When
            let got = subject.generation(
                config: config,
                cacheProfile: .onlyExternal,
                cacheSources: cacheSources,
                configuration: "Debug",
                cacheStorage: cacheStorage
            )

            // Then
            XCTAssertContainsElementOfType(
                got, TreeShakePrunedTargetsGraphMapper.self, after: FocusTargetsGraphMappers.self
            )
            XCTAssertContainsElementOfType(
                got,
                TreeShakePrunedTargetsGraphMapper.self,
                after: TargetsToCacheBinariesGraphMapper.self
            )
        }

        func test_focus_contains_the_cache_mapper() {
            // Given
            let config = Tuist.test()
            let cacheSources: Set<TargetQuery> = Set([.named("MyTarget")])

            // When
            let got = subject.generation(
                config: config,
                cacheProfile: .onlyExternal,
                cacheSources: cacheSources,
                configuration: "Debug",
                cacheStorage: cacheStorage
            )

            // Then
            XCTAssertContainsElementOfType(
                got, TargetsToCacheBinariesGraphMapper.self, after: FocusTargetsGraphMappers.self
            )
        }

        func test_automation_contains_the_tests_cache_graph_mapper() throws {
            // Given
            let config = Tuist.test()

            // When
            let got = subject.automation(
                config: config,
                ignoreBinaryCache: false,
                ignoreSelectiveTesting: false,
                testPlan: nil,
                includedTargets: [],
                excludedTargets: [],
                configuration: "Debug",
                cacheStorage: cacheStorage,
                destination: nil
            )

            // Then
            _ = XCTAssertContainsElementOfType(got, TestsCacheGraphMapper.self)
        }

        func test_automation_contains_the_filter_target_dependenies_tree_graph_mapper() throws {
            // Given
            let config = Tuist.test()

            // When
            let got = subject.automation(
                config: config,
                ignoreBinaryCache: false,
                ignoreSelectiveTesting: true,
                testPlan: nil,
                includedTargets: [],
                excludedTargets: [],
                configuration: "Debug",
                cacheStorage: cacheStorage,
                destination: nil
            )

            // Then
            XCTAssertContainsElementOfType(
                got, FocusTargetsGraphMappers.self, after: TestsCacheGraphMapper.self
            )
        }

        func test_automation_contains_the_tests_cache_tree_shaking_mapper() throws {
            // Given
            let config = Tuist.test()

            // When
            let got = subject.automation(
                config: config,
                ignoreBinaryCache: false,
                ignoreSelectiveTesting: false,
                testPlan: nil,
                includedTargets: [],
                excludedTargets: [],
                configuration: "Debug",
                cacheStorage: cacheStorage,
                destination: nil
            )

            // Then
            XCTAssertContainsElementOfType(
                got, TreeShakePrunedTargetsGraphMapper.self, after: FocusTargetsGraphMappers.self
            )
        }
    }
#endif
